--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 9fd0ae33d25690aeef2064d77624e75ace77302c
Parents : 0c07c1b
Author : Mark Qvist <mark@unsigned.io>
Date : 2026-04-20T17:16:39+02:00
Added support for Heltec v4.3 PA and LNA
Changes
3 files changed, 94 insertions(+), 18 deletions(-)
Diff
diff --git a/Boards.h b/Boards.h
index ef2b8c5..c059cd6 100644
--- a/Boards.h
+++ b/Boards.h
@@ -149,6 +149,10 @@
#endif
#endif
+ #define LORA_PA_UNKNOWN 0x00
+ #define LORA_PA_GC1109 0x01
+ #define LORA_PA_KCT8103L 0x02
+
#define HAS_DISPLAY false
#define HAS_BLUETOOTH false
#define HAS_BLE false
@@ -251,7 +255,7 @@
#define HAS_TCXO true
#define HAS_BUSY true
#define DIO2_AS_RF_SWITCH true
- #define OCP_TUNED 0x18
+ #define OCP_TUNED 0x28
const int pin_busy = 32;
const int pin_dio = 33;
const int pin_tcxo_enable = -1;
@@ -357,7 +361,7 @@
#define HAS_SLEEP true
#define PIN_WAKEUP GPIO_NUM_0
#define WAKEUP_LEVEL 0
- #define OCP_TUNED 0x18
+ #define OCP_TUNED 0x28
const int pin_btn_usr1 = 0;
@@ -399,8 +403,9 @@
#define HAS_LORA_LNA true
#define PIN_WAKEUP GPIO_NUM_0
#define WAKEUP_LEVEL 0
- #define OCP_TUNED 0x18
+ #define OCP_TUNED 0x28
#define Vext GPIO_NUM_36
+ #define LORA_PA_MODEL LORA_PA_UNKNOWN;
const int pin_btn_usr1 = 0;
@@ -422,14 +427,17 @@
#define LORA_LNA_GAIN 17
#define LORA_LNA_GVT 12
- #define LORA_PA_GC1109 true
#define LORA_PA_PWR_EN 7
- #define LORA_PA_CSD 2
- #define LORA_PA_CPS 46
+ #define LORA_PA_CSD 2 // Same pin on GC1109
+ #define LORA_PA_CPS 46 // Same pin on GC1109
+ #define LORA_PA_CTX 5 // Only used on KCT8103
#define PA_MAX_OUTPUT 28
#define PA_GAIN_POINTS 22
- #define PA_GAIN_VALUES 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7
+
+ #define LORA_LNA_KCT8103L_GAIN 21
+ const int PA_GC1109_VALUES[PA_GAIN_POINTS] = {11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7};
+ const int PA_KCT8103L_VALUES[PA_GAIN_POINTS] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 11, 11, 10, 9, 8, 7};
const int pin_cs = 8;
const int pin_busy = 13;
@@ -618,7 +626,7 @@
#define DIO2_AS_RF_SWITCH true
#define HAS_BUSY true
#define HAS_TCXO true
- #define OCP_TUNED 0x18
+ #define OCP_TUNED 0x28
#define HAS_DISPLAY true
#define HAS_CONSOLE true
@@ -921,7 +929,7 @@
// Default OCP value if not specified
// in board configuration
#ifndef OCP_TUNED
- #define OCP_TUNED 0x18
+ #define OCP_TUNED 0x28
#endif
#ifndef NP_M
diff --git a/Utilities.h b/Utilities.h
index 8892f59..f6fbac2 100644
--- a/Utilities.h
+++ b/Utilities.h
@@ -1287,11 +1287,33 @@ int getTxPower() {
}
#if HAS_LORA_PA
- const int tx_gain[PA_GAIN_POINTS] = {PA_GAIN_VALUES};
+ #if BOARD_MODEL == BOARD_HELTEC32_V4
+ bool pa_values_determined = false;
+ int tx_gain[PA_GAIN_POINTS] = {100};
+ #else
+ bool pa_values_determined = true;
+ const int tx_gain[PA_GAIN_POINTS] = {PA_GAIN_VALUES};
+ #endif
#endif
+extern uint8_t lora_pa_model;
+void determine_pa_values() {
+ #if BOARD_MODEL == BOARD_HELTEC32_V4
+ if (lora_pa_model == LORA_PA_GC1109) {
+ for (int i=0; i < PA_GAIN_POINTS; i++) { tx_gain[i] = PA_GC1109_VALUES[i]; }
+ pa_values_determined = true;
+ for (int i=0; i < PA_GAIN_POINTS; i++) { Serial.print(" "); Serial.printf("%d", tx_gain[i]); }
+ } else if (lora_pa_model == LORA_PA_KCT8103L) {
+ for (int i=0; i < PA_GAIN_POINTS; i++) { tx_gain[i] = PA_KCT8103L_VALUES[i]; }
+ pa_values_determined = true;
+ for (int i=0; i < PA_GAIN_POINTS; i++) { Serial.print(" "); Serial.printf("%d", tx_gain[i]); }
+ }
+ #endif
+}
+
int map_target_power_to_modem_output(int target_tx_power) {
#if HAS_LORA_PA
+ if (!pa_values_determined) { determine_pa_values(); }
int modem_output_dbm = -9;
for (int i = 0; i < PA_GAIN_POINTS; i++) {
int gain = tx_gain[i];
diff --git a/sx126x.cpp b/sx126x.cpp
index f8da5fa..97ca045 100644
--- a/sx126x.cpp
+++ b/sx126x.cpp
@@ -96,6 +96,14 @@
#define SPI spiModem
#endif
+#if HAS_LORA_PA
+ uint8_t lora_pa_model = LORA_PA_MODEL;
+#endif
+
+#if HAS_LORA_LNA
+ int lora_lna_gain = LORA_LNA_GAIN;
+#endif
+
extern SPIClass SPI;
#define MAX_PKT_LENGTH 255
@@ -358,7 +366,22 @@ int sx126x::begin(long frequency) {
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
#if HAS_LORA_PA
- #if LORA_PA_GC1109
+ if (lora_pa_model == LORA_PA_UNKNOWN) {
+ #if BOARD_MODEL == BOARD_HELTEC32_V4
+
+ pinMode(LORA_PA_PWR_EN, OUTPUT);
+ pinMode(LORA_PA_CSD, INPUT);
+ digitalWrite(LORA_PA_PWR_EN, HIGH); delay(5);
+ if (digitalRead(LORA_PA_CSD) == HIGH) {
+ lora_pa_model = LORA_PA_KCT8103L;
+ lora_lna_gain = LORA_LNA_KCT8103L_GAIN;
+ } else {
+ lora_pa_model = LORA_PA_GC1109;
+ }
+ #endif
+ }
+
+ if (lora_pa_model == LORA_PA_GC1109) {
// Enable Vfem_ctl for supply to
// PA power net.
pinMode(LORA_PA_PWR_EN, OUTPUT);
@@ -383,7 +406,26 @@ int sx126x::begin(long frequency) {
// is driven by the SX1262 DIO2
// pin directly, so we do not
// need to manually raise this.
- #endif
+
+ } else if (lora_pa_model == LORA_PA_KCT8103L) {
+ // Enable Vfem_ctl for supply to
+ // PA power net.
+ pinMode(LORA_PA_PWR_EN, OUTPUT);
+ digitalWrite(LORA_PA_PWR_EN, HIGH);
+
+ // Enable KCT8103L chip
+ pinMode(LORA_PA_CSD, OUTPUT);
+ digitalWrite(LORA_PA_CSD, HIGH);
+
+ // Enable receive LNA
+ pinMode(LORA_PA_CTX, OUTPUT);
+ digitalWrite(LORA_PA_CTX, LOW);
+
+ // On Heltec V4.3, the PA CPS pin
+ // is driven by the SX1262 DIO2
+ // pin directly, so we do not
+ // need to manually raise this.
+ }
#endif
return 1;
@@ -393,13 +435,15 @@ void sx126x::end() { sleep(); SPI.end(); _preinit_done = false; }
int sx126x::beginPacket(int implicitHeader) {
#if HAS_LORA_PA
- #if LORA_PA_GC1109
+ if (lora_pa_model == LORA_PA_GC1109) {
// Enable PA CPS for transmit
// digitalWrite(LORA_PA_CPS, HIGH);
// Disabled since we're keeping it
// on permanently as long as the
// radio is powered up.
- #endif
+ } else if (lora_pa_model == LORA_PA_KCT8103L) {
+ digitalWrite(LORA_PA_CTX, HIGH);
+ }
#endif
standby();
@@ -487,7 +531,7 @@ int ISR_VECT sx126x::currentRssi() {
executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1);
int rssi = -(int(byte)) / 2;
#if HAS_LORA_LNA
- rssi -= LORA_LNA_GAIN;
+ rssi -= lora_lna_gain;
#endif
return rssi;
}
@@ -503,7 +547,7 @@ int ISR_VECT sx126x::packetRssi() {
executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
int pkt_rssi = -buf[0] / 2;
#if HAS_LORA_LNA
- pkt_rssi -= LORA_LNA_GAIN;
+ pkt_rssi -= lora_lna_gain;
#endif
return pkt_rssi;
}
@@ -610,7 +654,7 @@ void sx126x::onReceive(void(*callback)(int)){
void sx126x::receive(int size) {
#if HAS_LORA_PA
- #if LORA_PA_GC1109
+ if (lora_pa_model == LORA_PA_GC1109) {
// Disable PA CPS for receive
// digitalWrite(LORA_PA_CPS, LOW);
// That turned out to be a bad idea.
@@ -618,7 +662,9 @@ void sx126x::receive(int size) {
// on and off too quickly. We'll keep
// it on permanently, as long as the
// radio is powered up.
- #endif
+ } else if (lora_pa_model == LORA_PA_KCT8103L) {
+ digitalWrite(LORA_PA_CTX, LOW);
+ }
#endif
if (size > 0) {
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────